﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace EntregaSO
{
    public class PlanificadorDistribuido : Planificador
    {
        public PlanificadorDistribuido(int pisos, int cantidadAscensores) : base(pisos, cantidadAscensores)
        {
        }
        protected override void CrearAscensores(int pisos, int cantidadAscensores)
        {
            this.ascensores.Clear();
            Ascensor a;
            //creo ascensores
            for (int i = 0; i < cantidadAscensores; i++)
            {
                a = new AscensorLOOK(pisos, this, "Ascensor" + i);
                ascensores.Add(a);
            }
        }

        public override void Planificar()
        {
            SolicitudAscensor s;
            int pisoActual;
            int distancia = int.MaxValue;
            int distanciaAux = int.MaxValue;
            int cantSolicitudes = int.MaxValue;

            lock (this)
            {
                //agregar monitor
                while (solicitudes.Count == 0)
                {
                    Monitor.Wait(this);
                }
                s = solicitudes.Pop();
                Monitor.PulseAll(this);
            }
            List<Ascensor> ascensoresSeleccionados = new List<Ascensor>();
            int cantidadPedidos = int.MaxValue;

            Ascensor asignacion = ascensores.First();
            
            foreach (Ascensor a in ascensores)
            {
                if (a.SiguientesPisos.Count < cantidadPedidos)
                {
                    ascensoresSeleccionados = new List<Ascensor>();
                    ascensoresSeleccionados.Add(a);
                    cantidadPedidos = a.SiguientesPisos.Count;
                }
                else
                {
                    if (a.SiguientesPisos.Count == cantidadPedidos)
                    {
                        ascensoresSeleccionados.Add(a);
                    }
                }
            }
            foreach (Ascensor a in ascensoresSeleccionados)
            {
                pisoActual = a.Piso;
                //busco el que tenga menos pedidos
                if (asignacion.SiguientesPisos.Count() < cantSolicitudes)
                {
                    cantSolicitudes = asignacion.SiguientesPisos.Count();
                    asignacion = a;
                }
                else
                {
                    //si tienen igual cantidad de pedidos evaluo la distancia al piso de solicitud
                    if (asignacion.SiguientesPisos.Count() == cantSolicitudes)
                    {
                        switch (a.Sentido)
                        {
                        case Ascensor.SentidoAscensor.Baja:
                            if (pisoActual > s.PisoOrigen)
                            {
                                distanciaAux = pisoActual - s.PisoOrigen;
                            }
                            else
                            {
                                distanciaAux = pisoActual + s.PisoOrigen;
                            }
                            break;
                        case Ascensor.SentidoAscensor.Sube:
                            if (pisoActual < s.PisoOrigen)
                            {
                                distanciaAux = s.PisoOrigen - pisoActual;
                            }
                            else
                            {
                                distanciaAux = (this.pisos - pisoActual) + (this.pisos - s.PisoOrigen);
                            }
                            break;
                        case Ascensor.SentidoAscensor.Quieto:
                            distanciaAux = Math.Abs(pisoActual - s.PisoOrigen);
                            break;
                        }
                        if (distanciaAux < distancia)
                        {
                            asignacion = a;
                            distancia = distanciaAux;
                        }
                    }
                }
            }
            asignacion.LlamarAscensor(s);
        }
    }
}
